package model.doe;

import model.util.CodingUtil;
import model.util.Factorial;

public class GaussLaguerreModan extends AbstractDoe {
	private static double w1, w2, sqd, e, MIN_VALUE_DOE = 0, MAX_VALUE_DOE = 0;
	private static int flag = 0, l1, p1, z1;
	private double doeStep;

	@Deprecated
	public GaussLaguerreModan() {
		w1 = 0;
		w2 = 0;
		sqd = 0;
		e = 0;
		l1 = 0;
		p1 = 0;
		z1 = 0;
		widthPointsNumber = 0;
		heightPointsNumber = 0;

	}

	public GaussLaguerreModan(double DoeWidth, double DoeHight, double DoeStep, double radius, int n1, int n2) {
		modlag_init(radius, n1, n2, 1, DoeHight, MIN_VALUE_DOE, MAX_VALUE_DOE);
		doeStep = DoeStep;
		widthPointsNumber = (int) (DoeWidth / DoeStep);
		heightPointsNumber = (int) (DoeHight / DoeStep);
		setDoeFormType(AbstractDoe.DOE_FORM_NON_SYMMETRIC);
	}

	@Override
	public double run(int i, int j) {
		return modlag(i * this.doeStep, j * this.doeStep);
	}


	/*
	 * Here is some of "C"-like methods
	 * 
	 * MAGIC!! DO NOT TOUCH!!
	 */

	private void modlag_init(double w, int p, int l, int z, double size, double min, double max) {

		// ****************** Parameter initialization ************************
		// w-beam radius
		// p-1 mode number
		// l-2 mode number
		double res, h = size / 256, s = 0;
		double min1, max1;
		double c = Factorial.getFactorial(p, l);
		int delt = 0;
		p1 = p;
		l1 = l;
		z1 = z;
		sqd = Math.sqrt(2.0);
		w1 = w;
		w2 = w * w;
		if (l == 0) {
			delt = 1;
		}
		e = 2.0 / (w * Math.sqrt(fkt(l) * c * Math.PI * (1 + delt)));
		flag = 1;
		min1 = modlag(0, 0);
		max1 = modlag(0, 0);
		for (s = 0; s <= size; s += h) {
			res = modlag(s, 0);
			if (res < min1)
				min1 = res;
			if (res > max1)
				max1 = res;
		}
		res = amin1(amin1((min1), -(min1)), amin1((max1), -(max1)));
		min = res;
		res = amax1(amax1((min1), -(min1)), amax1((max1), -(max1)));
		maximumValue = res;
		min = min - 0.2 * Math.abs(maximumValue - min);
		maximumValue = max + 0.2 * Math.abs(maximumValue - min);
	}

	private int fkt(int y) {
		int i = 1;
		for (int j = 1; j < y; j++) {
			i = i * (j + 1);
		}
		return i;
	}

	double modlag(double w, double h) {

		// ******* Complex function magnitude computation at single point *****

		if (flag == 0) {
			System.out.println(" parameters must be initializated ");
			return 0;
		}
		double r, fi, res;
		double psi, param, par1;// ,par2;
		r = Math.sqrt(w * w + h * h);
		if (h == 0 && w == 0)
			fi = 0;
		else
			fi = Math.atan2(h, w);
		param = -(r * r) / w2;
		res = Math.sin(l1 * fi);
		if (z1 == 1)
			res = Math.cos(l1 * fi);
		if (l1 == 0)
			par1 = 1;
		else
			par1 = Math.pow(r * sqd / w1, l1);
		psi = e * par1 * lag(2 * r * r / w2, l1, p1) * Math.exp(param) * res;
		return psi;
	}

	private double lag(double arg, int l, int p) {

		// ************* Laguerre polynom
		// ****************************************

		double lag0 = 1, lag1 = l + 1 - arg;
		double lg = 0;
		if (p == 0) {
			return lag0;
		}
		if (p == 1) {
			return lag1;
		}

		for (int i = 2; i < p + 1; i++) {
			lg = (2 * (i - 1) + l + 1 - arg) * lag1 - ((i - 1) + l) * lag0;
			lg = lg / i;
			lag0 = lag1;
			lag1 = lg;
		}
		return lg;
	}

	private double amax1(double a, double b) {

		double m = a;
		if (b > a)
			m = b;
		return m;
	}

	private double amin1(double a, double b) {

		double m = a;
		if (b < a)
			m = b;
		return m;
	}

}
